# This file contains the classes Account, Admin, User, Shift, Calendar, and SlotHandler. It also contains the functions createTestCalOpen, createTestCalClosed, and clearDb. 

import webapp2
from google.appengine.ext import db
from google.appengine.ext.db import polymodel



# Class Calendar: name, key, key of admin, status (open or closed boolean), 
# list of shifts, (closing date eventually - for now, manual)
# Short cal
class Calendar(db.Model):
	name = db.StringProperty()
	admin = db.ReferenceProperty()
	closed = db.BooleanProperty()
	shifts = db.ListProperty(db.Key)
	
# Class Account: Name, email, key
# PolyModel is a superclass
# Short - ac
class Account(polymodel.PolyModel):
# All models have a key assigned when they are put()
# You can retrieve this key using Model.key()
# "We're going to have to have exams every class" -ALK 2/6/12
	email = db.EmailProperty()
	name = db.StringProperty()
	shifts = db.ListProperty(db.Key)
	assigned = db.ListProperty(db.Key)
	
# Subclass Admin: password, calendar key
# Short - ad
class Admin(Account):
	password = db.StringProperty()
	cal = db.ReferenceProperty(Calendar)

# Subclass User 
# Short - usr
class User(Account):
	pass	

# Class Shift: timeframe (list of blocks), assignment (NULL until closed), up 
# for grabs, sub, list of available users' keys, key
# Short - sh
class Shift(db.Model):
	# Time is more complicated. So day 1 = Sunday
	day = db.IntegerProperty()
	time = db.IntegerProperty()
	accounts = db.ListProperty(db.Key)
	assignment = db.ReferenceProperty(Account, collection_name="shift_assignment_set")
	# "I have tenure. That means I can say whatever the hell I want to say." -ALK 2/8/12 
	sub = db.ReferenceProperty(Account, collection_name="shift_sub_set")
	up4grab =  db.BooleanProperty()
	cal = db.ReferenceProperty(Calendar)	

class SlotHandler(webapp2.RequestHandler):
# Print functions must be defined in SlotHandler class to use self.response.out.write function
	def printAc(self,ac):
		self.response.out.write("<b>Account name</b>: %s --------------------------<b>Email</b>: %s" % (ac.name, ac.email))
		self.response.out.write('<ul>\n')
		for shk in ac.shifts:
			sh = Shift.get(shk)
			self.response.out.write('<li>')
			self.response.out.write("Shift day: %s time: %s" % (sh.day, sh.time))
			self.response.out.write('</li>\n')
		self.response.out.write('</ul>\n')
			
	def printSh(self,sh):
		self.response.out.write('Shift day: %s - time: %s <br/><b>Available accounts</b>' % (sh.day, sh.time))
		self.response.out.write('<ul>\n')
		for ack in sh.accounts:
			ac = Account.get(ack)
			self.response.out.write('<li>')
			self.printAc(ac)
			self.response.out.write('</li>\n')
		self.response.out.write('</ul>\n')
			
	def printCal(self,cal):
		self.response.out.write("<h2>Calendar name: %s</h2>" % cal.name)
		self.response.out.write("<h3>Shifts</h3>")
		self.response.out.write('<ol>\n')
		for shk in cal.shifts:
			sh = Shift.get(shk)
			self.response.out.write('<li>')
			self.printSh(sh)
			self.response.out.write('</li>\n')
		self.response.out.write('</ol>\n')
		
	def printCalShifts(self,cal):
		self.response.out.write("""
		<h3>Shifts</h3>
		""" % (cal.name, Admin.get(cal.admin).name))
		self.response.out.write('<ol>\n')
		for shk in cal.shifts:
			sh = Shift.get(shk)
			self.response.out.write('<li>')
			self.printSh(sh)
			self.response.out.write('</li>\n')
		self.response.out.write('</ol>\n')
				
	def printDb(self):
		self.response.out.write('<h1>Calendars</h1>')
		for cal in Calendar.all():
			self.printCal(cal)
		self.printShifts(cal)
		self.response.out.write('<h1>Shifts</h1>')
		for sh in Shift.all():
			self.printSh(sh)
		self.response.out.write('<h1>Accounts</h1>')
		for ac in Account.all():
			self.printAc(ac)
			self.response.out.write('<br/>')
			
def addAdmin(ad, cal):
	ad.cal = cal.key()
	ad.put()
	cal.admin = ad.key()
	cal.put() 

def addAvail(ac, sh):
	sh.accounts.append(ac.key())
	sh.put()
	ac.shifts.append(sh.key())
	ac.put()

	
# These functions can be defined outside of the MainPage class because they do 
# not require self.response.out.write function
# Make the open test calendar

def addShift(sh, cal):
	sh.cal = cal.key()
	sh.put()
	cal.shifts.append(sh.key())
	cal.put()
	
def delShift(sh, cal):
	cal.shifts.remove(sh.key())
	sh.delete()

def addAssign(ac, sh):
	sh.assignment = ac.key()
	sh.put()
	ac.assigned.append(sh.key)
	ac.put()
	

def createTestCalOpen():
	# Make the test calendar (open)
	# Add users to datastore
	admin1 = Admin(email='admin1@gmail.com', name='admin1', password='y35')
	admin1.put()
	user2 = User(email='user2@gmail.com', name='user2')
	user2.put()
	user3 = User(email='user3@gmail.com', name='user3')
	user3.put()
	
	sh1 = Shift()
	sh1.day = 1
	sh1.time = 12
	addAvail(admin1, sh1)
	addAvail(user2, sh1)
	sh1.up4grab = False
	sh1.put()
	
	sh2 = Shift()
	sh2.day = 3
	sh2.time = 8
	addAvail(admin1, sh2)
	addAvail(user2, sh2)
	addAvail(user3, sh2)
	sh2.up4grab = False
	sh2.put()
	
	sh3 = Shift()
	sh3.day = 6
	sh3.time = 20
	addAvail(admin1, sh3)
	sh3.up4grab = False
	sh3.put()
	
	cal = Calendar()
	cal.name = "Test Calendar open"
	cal.put()
	addAdmin(admin1, cal)
	addShift(sh1, cal)
	addShift(sh2, cal)
	addShift(sh3, cal)
	cal.closed = False
	
	cal.put()

# Make the closed test calendar
def createTestCalClosed():
	users = [u for u in User.all()]
	user2 = users[0]
	user3 = users[1]
	admin1 = Admin.all().get()
	
	sh1 = Shift()
	sh1.day = 1
	sh1.time = 12
	addAvail(admin1, sh1)
	addAvail(user2, sh1)
	sh1.assignment = admin1.key()
	sh1.put()
	
	sh2 = Shift()
	sh2.day = 3
	sh2.time = 8
	addAvail(user2, sh2)
	addAvail(user3, sh2)
	sh2.assignment = user2.key()
	sh2.sub = user3.key()
	sh2.put()
	
	sh3 = Shift()
	sh3.day = 6
	sh3.time = 20
	addAvail(admin1, sh3)
	addAvail(user3, sh3)
	sh3.assignment = admin1.key()
	sh3.up4grab = True
	sh3.put()
	
	cal = Calendar()
	cal.name = "Test Calendar closed"
	cal.admin = admin1.key()
	
	addShift(sh1, cal)
	addShift(sh2, cal)
	addShift(sh3, cal)
	cal.closed = True
	
	cal.put()

# Run this to clear the database
def clearDb():
	for ac in Account.all():
		ac.delete()
	for sh in Shift.all():
		sh.delete()
	for cal in Calendar.all():
		cal.delete()



# Clone an entity 
def clone_entity(e, **extra_args):
	"""Clones an entity, adding or overriding constructor attributes.
	
	The cloned entity will have exactly the same property values as the original
	entity, except where overridden. By default it will have no parent entity or
	key name, unless supplied.
	
	Args:
	  e: The entity to clone
	  extra_args: Keyword arguments to override from the cloned entity and pass
	    to the constructor.
	Returns:
	  A cloned, possibly modified, copy of entity e.
	
	http://stackoverflow.com/questions/2687724/copy-an-entity-in-google-app-eng
	ine-datastore-in-python-without-knowing-property
	"""
	klass = e.__class__
	props = dict((k, v.__get__(e, klass)) for k, v in klass.properties().iteritems())
	props.update(extra_args)
	return klass(**props)